enum SnakeTileType
{
    SnakeTileEmpty,
    SnakeTileWall,
    SnakeTileSelf,
    SnakeTileConsumable,
};

namespace SnakeConstants
{
    static const int frameAdvanceCount = 1;
    static const int startSnakeLength = 1;
    static const int spawnPeriod = 20;

    static const int boardWidth = 23;
    static const int boardHeight = Constants::NESGridHeight;

    static const int levelCount = 6;
    static const Vec2i exitCoord = Vec2i(SnakeConstants::boardWidth / 2, SnakeConstants::boardHeight - 1);
    static const float AIStupidity = 0.0f;
}

struct SnakeTileEntry
{
    inline bool Passable() const
    {
        return (type == SnakeTileEmpty || type == SnakeTileConsumable);
    }

    int spriteIndex;
    SnakeTileType type;
};

struct SnakeLevel
{
    void Load(const Bitmap &bmp, int spriteBlock);
    Grid<SnakeTileEntry> tiles;
};

class GameSnake : public Game
{
public:
    void Init();
    string Name()
    {
        return "snake";
    }
    void NewGame();
    void Step(ControllerState &controller);
    ControllerState SimpleAI();

private:
    void MakeFrameSprites();
    void ResetLevel();
    void IncrementScore();

    //
    // Useful functions
    //
    int ConsumableCount() const;
    bool PassableOrSnakeHead(const SnakeTileEntry &entry) const;
    int EmptyRadius(const Vec2i &coord) const;

    inline int VertexFromCoord(int x, int y) const
    {
        return y * SnakeConstants::boardWidth + x;
    }
    inline int VertexFromCoord(const Vec2i &c) const
    {
        return c.y * SnakeConstants::boardWidth + c.x;
    }
    inline Vec2i CoordFromVertex(int v) const
    {
        return Vec2i(v % SnakeConstants::boardWidth, v / SnakeConstants::boardWidth);
    }


    //
    // Asset data
    //
    vector<SnakeLevel> _levels;

    //
    // Game state
    //
    Grid<SnakeTileEntry> _tiles;
    list<Vec2i> _snakeTiles;
    Vec2i _snakeDirection;
    int _maxSnakeLength;
    
    int _advanceFramesLeft;
    int _spawnFramesLeft;

    int _framesSinceGameReset;
    int _score;
    int _level;
    int _timeLeft;
    int _lives;
    int _applesNeeded;

    //
    // Sprite IDs
    //
    int _spriteBlock;
    int _spriteTail;
    int _spriteBody;
    int _spriteHeadU;
    int _spriteHeadR;
    int _spriteHeadD;
    int _spriteHeadL;
    int _spriteApple;
    int _spriteGoldenApple;
    int _spriteBadApple;
};
